package tools

import (
	"math"
)

type LocationConverter struct {
}

const (
	PI              = 3.14159265358979324
	X_PI            = 3.14159265358979324 * 3000.0 / 180.0
	EE              = 0.00669342162296594323
	AA              = 6378245.0
	GPS_NO_LOCATION = 0.0000000
)

var lbsConvert LocationConverter

/// <summary>
/// 将世界标准地理坐标(GPS:WGS-84)转换成中国国测局地理坐标(火星:GCJ-02)
/// </summary>
/// <param name="longitude">GPS经度值。</param>
/// <param name="latitude">GPS纬度值。</param>
/// <remarks>只在中国大陆的范围的坐标有效，以外直接返回世界标准坐标。</remarks>
/// <returns>GCJ-02坐标实例。</returns>
func Convert84To02(longitude float64, latitude float64) (float64, float64) {
	return lbsConvert.EncryptGCJ02(longitude, latitude)
}

/// <summary>
/// 将中国国测局地理坐标(火星:GCJ-02)转换成世界标准地理坐标(GPS:WGS-84)
/// </summary>
/// <remarks>此接口有1-2米左右的误差，需要精确的场景慎用。</remarks>
/// <param name="longitude">GCJ-02经度值。</param>
/// <param name="latitude">GCJ-02纬度值。</param>
/// <returns>WGS-84坐标实例。</returns>
func Convert02To84(longitude float64, latitude float64) (float64, float64) {
	if lbsConvert.IsZero(longitude, latitude) {
		return longitude, latitude
	}

	return lbsConvert.DecryptGCJ02(longitude, latitude)
}

/// <summary>
/// 将世界标准地理坐标(GPS:WGS-84)转换成百度地理坐标(BD-09)
/// </summary>
/// <param name="longitude">GPS经度值。</param>
/// <param name="latitude">GPS纬度值。</param>
/// <returns>BD-09坐标实例。</returns>
func Convert84To09(longitude float64, latitude float64) (float64, float64) {
	if lbsConvert.IsZero(longitude, latitude) {
		return longitude, latitude
	}

	//将WGS-84转换为GCJ-02
	bufferLongitude, bufferLatitude := lbsConvert.EncryptGCJ02(longitude, latitude)

	//将GCJ-02转换BD-09坐标。
	return lbsConvert.EncryptBD09(bufferLongitude, bufferLatitude)
}

/// <summary>
/// 将百度地理坐标(BD-09)转换成世界标准地理坐标(GPS:WGS-84)
/// </summary>
/// <remarks>此接口有1－2米左右的误差，需要精确定位情景慎用</remarks>
/// <param name="longitude">BD-09坐标经度值。</param>
/// <param name="latitude">BD-09坐标纬度值。</param>
/// <returns>WGS-84坐标实例。</returns>
func Convert09To84(longitude float64, latitude float64) (float64, float64) {
	if lbsConvert.IsZero(longitude, latitude) {
		return longitude, latitude
	}

	//将BD-09转换为GCJ-02
	tmp_longitude, tmp_latitude := Convert09To02(longitude, latitude)

	//将GCJ-02转换WGS-84坐标。
	return lbsConvert.DecryptGCJ02(tmp_longitude, tmp_latitude)
}

/// <summary>
/// 将百度地理坐标(BD-09)转换成中国国测局地理坐标(火星:GCJ-02)
/// </summary>
/// <param name="longitude">BD-09经度值。</param>
/// <param name="latitude">BD-09纬度值。</param>
/// <returns>GCJ-02坐标实例。</returns>
func Convert09To02(longitude float64, latitude float64) (float64, float64) {
	if lbsConvert.IsZero(longitude, latitude) {
		return longitude, latitude
	}

	return lbsConvert.DecryptBD09(longitude, latitude)
}

/// <summary>
/// 将中国国测局地理坐标(火星:GCJ-02)转换成百度地理坐标(BD-09)
/// </summary>
/// <param name="longitude">GCJ-02坐标经度值。</param>
/// <param name="latitude">GCJ-02坐标纬度值。</param>
/// <returns>BD-09坐标实例。</returns>
func Convert02To09(longitude float64, latitude float64) (float64, float64) {
	if lbsConvert.IsZero(longitude, latitude) {
		return longitude, latitude
	}

	return lbsConvert.EncryptBD09(longitude, latitude)
}

/// <summary>
/// 将世界标准地理坐标(GPS:WGS-84)加密成中国国测局地理坐标(火星:GCJ-02)
/// </summary>
/// <remarks>只在中国大陆的范围的坐标有效，以外直接返回世界标准坐标。</remarks>
/// <param name="longitude">GPS经度。</param>
/// <param name="latitude">GPS纬度。</param>
/// <returns>GCJ-02坐标实例。</returns>
func (this *LocationConverter) EncryptGCJ02(longitude float64, latitude float64) (float64, float64) {
	//如果超出大陆的坐标范围，则直接返回世界标准坐标。
	if this.IsOutOfChina(longitude, latitude) {
		return longitude, latitude
	}

	//开始转换经度值。
	bufferLongitude := this.TransformLongitude(longitude-105.0, latitude-35.0)

	//开始转换纬度值。
	bufferLatitude := this.TransformLatitude(longitude-105.0, latitude-35.0)

	//获取弧度。
	radLatitude := latitude / 180.0 * PI
	magic := math.Sin(radLatitude)
	magic = 1 - EE*magic*magic
	sqrtMagic := math.Sqrt(magic)
	bufferLongitude = (bufferLongitude * 180.0) / (AA / sqrtMagic * math.Cos(radLatitude) * PI)
	bufferLatitude = (bufferLatitude * 180.0) / ((AA * (1 - EE)) / (magic * sqrtMagic) * PI)

	//返回结果。
	return longitude + bufferLongitude, latitude + bufferLatitude
}

/// <summary>
/// 将中国国测局地理坐标(火星:GCJ-02)解密成世界标准地理坐标(GPS:WGS-84)
/// </summary>
/// <remarks>此接口有1-2米左右的误差，需要精确的场景慎用。</remarks>
/// <param name="longitude">GPS经度。</param>
/// <param name="latitude">GPS纬度。</param>
/// <returns>WGS-84坐标实例。</returns>
func (this *LocationConverter) DecryptGCJ02(longitude float64, latitude float64) (float64, float64) {
	bufferLongitude, bufferLatitude := this.EncryptGCJ02(longitude, latitude)
	bufferLongitude = bufferLongitude - longitude
	bufferLatitude = bufferLatitude - latitude

	//返回结果。
	return longitude - bufferLongitude, latitude - bufferLatitude
}

/// <summary>
/// 将中国国测局地理坐标(火星:GCJ-02)加密成百度地理坐标(BD-09)
/// </summary>
/// <param name="longitude">GCJ-02经度值。</param>
/// <param name="latitude">GCJ-02纬度值。</param>
/// <returns>BD-09坐标实例。</returns>
func (this *LocationConverter) EncryptBD09(longitude float64, latitude float64) (float64, float64) {
	x := longitude
	y := latitude
	z := math.Sqrt(x*x+y*y) + 0.00002*math.Sin(y*X_PI)
	theta := math.Atan2(y, x) + 0.000003*math.Cos(x*X_PI)

	//返回结果。
	return z*math.Cos(theta) + 0.0065, z*math.Sin(theta) + 0.006
}

/// <summary>
/// 将百度地理坐标(BD-09)解密成中国国测局地理坐标(火星:GCJ-02)
/// </summary>
/// <param name="longitude">BD-09经度值。</param>
/// <param name="latitude">BD-09纬度值。</param>
/// <returns>GCJ-02坐标实例。</returns>
func (this *LocationConverter) DecryptBD09(longitude float64, latitude float64) (float64, float64) {
	x := longitude - 0.0065
	y := latitude - 0.006
	z := math.Sqrt(x*x+y*y) - 0.00002*math.Sin(y*X_PI)
	theta := math.Atan2(y, x) - 0.000003*math.Cos(x*X_PI)

	//返回结果。
	return z * math.Cos(theta), z * math.Sin(theta)
}

/// <summary>
/// 判断指定的GPS经纬度是否超出中国大陆范围。
/// </summary>
/// <param name="longitude">经度值。</param>
/// <param name="latitude">纬度值。</param>
/// <returns>验证结果。</returns>
func (this *LocationConverter) IsOutOfChina(longitude float64, latitude float64) bool {
	if longitude < 72.004 || longitude > 137.8347 {
		return true
	}

	if latitude < 0.8293 || latitude > 55.8271 {
		return true
	}

	return false
}

/// <summary>
/// 判断指定的GPS经纬度是否为0。为0表示GPS设备未定到位
/// </summary>
/// <param name="longitude">经度值。</param>
/// <param name="latitude">纬度值。</param>
/// <returns>验证结果。</returns>
func (this *LocationConverter) IsZero(longitude float64, latitude float64) bool {
	if longitude == GPS_NO_LOCATION && latitude == GPS_NO_LOCATION {
		return true
	}

	return false
}

/// <summary>
/// 转换GPS经度值。
/// </summary>
/// <param name="longitude">经度值。</param>
/// <param name="latitude">纬度值。</param>
/// <returns>转换后的经度值。</returns>
func (this *LocationConverter) TransformLongitude(longitude float64, latitude float64) float64 {
	result := 300.0 + longitude + 2.0*latitude + 0.1*longitude*longitude + 0.1*longitude*latitude + 0.1*math.Sqrt(math.Abs(longitude))

	result += (20.0*math.Sin(6.0*longitude*PI) + 20.0*math.Sin(2.0*longitude*PI)) * 2.0 / 3.0
	result += (20.0*math.Sin(longitude*PI) + 40.0*math.Sin(longitude/3.0*PI)) * 2.0 / 3.0
	result += (150.0*math.Sin(longitude/12.0*PI) + 300.0*math.Sin(longitude/30.0*PI)) * 2.0 / 3.0

	return result
}

/// <summary>
/// 转换GPS纬度值。
/// </summary>
/// <param name="longitude">经度值。</param>
/// <param name="latitude">纬度值。</param>
/// <returns>转换后的纬度值。</returns>
func (this *LocationConverter) TransformLatitude(longitude float64, latitude float64) float64 {
	result := -100.0 + 2.0*longitude + 3.0*latitude + 0.2*latitude*latitude + 0.1*longitude*latitude + 0.2*math.Sqrt(math.Abs(longitude))

	result += (20.0*math.Sin(6.0*longitude*PI) + 20.0*math.Sin(2.0*longitude*PI)) * 2.0 / 3.0
	result += (20.0*math.Sin(latitude*PI) + 40.0*math.Sin(latitude/3.0*PI)) * 2.0 / 3.0
	result += (160.0*math.Sin(latitude/12.0*PI) + 320*math.Sin(latitude*PI/30.0)) * 2.0 / 3.0

	return result
}
